Chapter w1: BBEdit Pro for HTML

BBEdit was a relatively unknown application that was used primarily by serious programmers working in CodeWarrior and other C++ compilers until the World Wide Web arrived on the scene. With HTML files being merely glorified text files, a great many people suddenly needed a powerful and complete text-based editor on the Mac. BBEdit was there waiting all along.

In its latest incarnations, BBEdit includes a complete suite of text-based HTML tools. BBEdit's extensive AppleScript support extends to these new HTML tools, making BBEdit a powerhouse for scripted HTML creation and modification.



Fig. w1.1  Entering some sample text into a new file in BBEdit

Scripting BBEdit Pro

For all of the following scripts that use BBEdit, you will need at least a couple simple text files. You can find some old SimpleText-based Read Me files on your hard drive, or you can follow the steps below to quickly create a couple of files. In either case, place your text files in folder named Text Files in a readily accessible place like the Desktop.

Scripting BBEdit

All the scripts that follow were designed and tested for BBEdit 4.5. BBEdit is published by Bare Bones Software, who can be reached at http://www.barebones.com/.

To get ready for the following AppleScripts for BBEdit:

  1. Launch BBEdit and create a new file.
  2. Type in the sample text "The quick brown fox jumps over the lazy dog." and then save the file as "1" in the Save dialog, as shown in Figure w1.1.
  3. Create another new file, type in slightly different sample text, and save it as "2" as shown in Figure w1.2.
  4. Now we're ready to move on to the scripts.

BBEdit is attachable



Fig. w1.1  Entering some sample text into a new file in BBEdit
 
 


Fig. w1.2  Entering more sample text into a second new file in BBEdit.

Using pattern matching to search for and replace text

One of BBEdit's most powerful features is its ability to search for patterns of text and then replace them with new text. This pattern searching feature is derived from abilities found in UNIX systems and is known by the UNIX term "grep." The power of this feature is simply too great to describe in a few words, but perhaps an example will serve to illustrate the potential of grep searches.

Code w1.1 finds every instance in a file of the word "the" followed by a space and another word and replaces it with the words "the crazy." We won't need to manually open any file in BBEdit since our script will handle this for us. This script can be run from the Script Editor.

A quick guide to grep patterns

.

matches any character.

#

matches any digit.

^

matches the beginning of a line.

$

matches the end of a line.

[abc]

matches any character a, b, or c.

[^abc]

matches any character except a, b, or c.

[a-e]

matches any character from a to e.

\t

matches a tab.

\r

matches a return.

A*

matches zero or more A's.

A+

matches one or more A's.

A?

matches zero or one A.

(A)

creates a group match that can be referred to in the replace string by \n, where n refers to the nth instance of (), i.e. \1, \2.

To use grep pattern matching to search for and replace text in a file:

  1. set myFile to (choose file)

    First we prompt the user to select the file to open and store the reference to that file (as an alias) in the variable myFile.

  2. tell application "BBEdit 4.5"

    Now we let AppleScript know that we want to send commands to BBEdit.

  3.   open myFile

    Then we have BBEdit open the file referred to by myFile.

  4.   replace Every Occurrence searching for "The [a-z]+" using "the crazy" with grep and start at top

    This one simple line does something pretty incredible thanks to grep pattern matching. It finds every instance of the literal text "the" followed by a space and another word (which is defined in grep as one or more characters between a and z). Once it finds an instance, BBEdit replaces the characters with the literal text "the crazy".

  5. end tell

Code w1.1. Using grep search and replace in BBEdit to match patterns and replace them.


Note: Lines wrapped with red arrows should be typed on a single line.
 


Fig. w1.3  Our sample text file in BBEdit after the script has changed the word immediately following each "the" to "crazy."

Doing batch search and replace on many files

For our next BBEdit trick, we'll create a script that will become a drag-and-drop application to batch process any number of text files in any number of nested folders (see Code w1.2). The heart of our routine will be the same replace command used in Code w1.1.

Keep in mind that once you've opened the file in BBEdit you can issue any number of replace commands to modify the open file before your script saves the changed file and moves on.

Once you've entered this script in the Script Editor, save it as an application on your Desktop, checking the Never Show Startup Screen option. This will make the script execute immediately when a user double-clicks its icon or drops files onto it. Then either drag and drop the Text Files folder we created earlier onto it or double-click it to be prompted to find a folder to process. Figure w1.4 shows what happens if you double-click the script application instead of dropping a folder onto it.

To batch search and replace text in many files:

  1. on run

    To start, we define an on run handler to deal with occasions when the user double-clicks the script application instead of dropping a folder onto it.

  2. set myFolder to choose folder with prompt "Select a folder to batch:"

    If the script was run by double-clicking, we prompt the user to select a folder to batch process and store the reference to that folder in the variable myFolder.

  3. batchProcess(myFolder) of me
    end run

    Then, we call the main function, batchProcess, passing to it the reference to the folder contained in myFolder.

  4. on open myFolder
      batchProcess(myFolder) of me
    end open

    If the user drops a folder onto the script application, the Finder will pass a reference to the folder to our script in the variable myFolder. Then, we call the main function as above, passing the reference to the folder.

  5. on batchProcess(myCurrentFolder)

    Here, we begin our function, batchProcess, and tell AppleScript to store any value passed to it in the variable myCurrentFolder.

  6. tell application "Finder"

    Next, we let AppleScript know that we want to talk to the Finder.

  7. set myFolderContents to list folder myCurrentFolder without invisibles

    We ask the Finder to return a list of items contained in the folder referred to by myCurrentFolder and store it in myFolderContents.

  8. repeat with myFile in myFolderContents

    Now we begin a repeat loop, placing one item from the list myFolderContents into the variable myFile for each loop until we reach the end of the list.

  9. if kind of alias (myCurrentFolder & myFile as text) is "folder" then

    We ask the Finder for the type of item myFile in the folder referred to by myCurrentFolder. If it's a folder then our script will execute the next line.

  10. batchProcess(myCurrentFolder & myFile as text) of me

    If our function encounters another folder, it will call itself, passing a reference to the enclosed folder. In this way, our script processes all folders nested inside of the original.

  11. else
      tell application "BBEdit 4.5"

    If the item myFile in the folder isn't itself a folder, we begin talking to BBEdit.

  12. open (myCurrentFolder & myFile as text)

    First, we ask BBEdit to open the item myFile inside of the folder myCurrentFolder. We have to coerce the values to text to make sure AppleScript doesn't evaluate them as a list, which would generate an error.

  13. replace Every Occurrence searching for "(The )[a-z]+" using "the crazy" with grep and start at top

    Now BBEdit replaces our search string with the replace text using grep pattern matching and starting at the beginning of the document.

  14. close front window saving in (myCurrentFolder & myFile as text)
    end tell
    end if
    end repeat
    end tell
    end batchProcess

    Finally, we have BBEdit save the changed file over itself and conclude our conversation, our if statement, our repeat loop, and our function.

Code w1.2. This elaborate script allows the user to either drag and drop or select folders to batch search and replace within BBEdit, saving the changes.


Note: Lines wrapped with red arrows should be typed on a single line.


Fig. w1.4 When this script is run by double-clicking, the on run handler displays this dialog to allow the user to select a folder to batch process.

Finding differences between two files

There are often times when you'd like to compare two files to see if they are different. BBEdit makes this very easy with its find differences statement.

Code w1.3 performs a comparison of two files specified by the user and reports whether they are the same or different. Figure w1.5 shows the result of a comparison of our two sample text files, 1 and 2.

This script can be run from the Script Editor.

To find differences between two files:

  1. set myFile1 to (choose file with prompt "Select first file for comparison:")

    We begin this script by prompting the user to select the first file for comparison and saving the reference to the file in the variable myFile1.

  2. set myFile2 to (choose file with prompt "Select second file:")

    Then we prompt for the second file and save its reference in myFile2.

  3. set areDifferent to (find differences new myFile1 old myFile2)

    Here we ask BBEdit to compare the two files and return the result of the comparison as a Boolean (true or false) value in the variable areDifferent.

  4. close every window

    Next, we have BBEdit close the Find Differences windows it opened in the last command.

  5. if areDifferent then
      display dialog "These files are different."else
      display dialog "These files are the same."
    end if

    Finally, we evaluate the value of areDifferent and display one of two dialogs based on its value.

Tips

Code w1.3. Performs a comparison of two files specified by the user and reports whether they are the same or different.


Note: Lines wrapped with red arrows should be typed on a single line.


Fig. w1.5  The dialog displayed by Code w1.3 for nonmatching text files.

Labeling changed files in the Finder

So now that we can slice, dice, and puree our text files with BBEdit and AppleScript, wouldn't it be nice to keep track of what's changed in a simple and clear manner? And what could be easier than labeling any changes in the Finder so that we can see what's happened at a glance?

Code w1.4 replaces any instances of "dog" that it finds in a text file with "cat", labeling the file in the Finder if any changes were made. Try running this script from the Script Editor and selecting one of our sample text files. Figure w1.6 shows the result.

More Finder scripting

To label changed files in the Finder:

  1. open myFile

    Here we tell BBEdit to open the file referred to by myFile, which we prompted the user for at the beginning of the script.

  2. set myFound to (find "dog" with start at top)

    Next, we test for a match for the search string ³dog² and store the Boolean result in the variable myFound. We need to use the find command first to find out if the string match exists. It returns a Boolean value to let us know if the string exists. The replace command doesn't return any such value.

  3. if myFound then

    We test myFound to see if it contains the value true.

  4. replace Every Occurrence searching for "dog" using "cat" without grep and start at top

    If myFound is true, then we replace the literal text "dog" with "cat".

  5. close front window saving in myFile

    Then we close the window, sav

  6. ing over the original file.

  7. 6. tell application "Finder"
      set the label index of myFile to 2
    end tell

    Finally, we tell the Finder to set the label of the file to indicate that it's been changed.

  8. 7. else
      close front window saving in myFile
    end if
    end tell

    If the search string wasn't found in the file, we simply close its window in BBEdit.

Borrowing from this script

Code w1.4. This script replaces any instances of "dog" in finds in a text file with "cat", labeling changed files in the Finder.


Note: Lines wrapped with red arrows should be typed on a single line.
 

Fig. w1.6  A view of the Finder window showing the text file's icon with its label after our script has run.

Deleting matching lines of text

Code w1.5 traverses every line of text in a file sequentially, searching for lines below the current line that match it. All matching lines are replaced with a null string, eliminating them. This script is useful for stripping duplicate entries from flat text file databases.

Figure w1.7 shows the active window in BBEdit as it appears while the script is running.

To delete matching lines of text:

  1. tell application "BBEdit 4.5"
      activate
      repeat with i from 1 to ((number of lines in document 1) - 1)

    We begin by telling BBEdit to come to the front. Then we start looping through each line of the frontmost open window in BBEdit.

  2. set myLine to line i of document 1

    Next, we set myLine to the contents of the current line in our loop.

  3. go to line i + 1

    Then we tell BBEdit to go to the line just after the current line.

  4. replace Every Occurrence searching for myLine using "" without start at top and grep end repeatend tell

    Now we have BBEdit search from the line below the current line to the end of the document without using grep pattern matching, replacing any duplicate lines it finds with null strings.

Use the Scripts menu

Code w1.5. This script traverses every line of text in a file sequentially, searching for lines below the current line that match it.


Note: Lines wrapped with red arrows should be typed on a single line.

Fig. w1.7  The active window in BBEdit as it appears while the script is running.

Cleaning visual editors' HTML code

Code w1.6 is a variation on our previous batch replace script (Code w1.2). It is a drag-and-drop application that can batch process any number of text files in any number of nested folders.

This script removes all tabs and multiple spaces in the files, saving disk space by eliminating the unneeded code indentation added by most visual editors.

Figure w1.8 displays the Get Info windows for a file before and after processing, showing the file size difference in bytes.

To clean visual editors' HTML code:

  1. on run

    To start, we define an on run handler to deal with occasions when the user double-clicks the script application instead of dropping a folder onto it.

  2. set myFolder to choose folder with prompt "Select a folder to clean:"

    If the script was run by double-clicking, we prompt the user to select a folder to batch process and store the reference to that folder in the variable myFolder.

  3. batchProcess(myFolder) of me
    end run

    Then, we call the main function, batchProcess, passing to it the reference to the folder contained in myFolder.

  4. on open myFolder
      batchProcess(myFolder) of me
    end open

    If the user drops a folder onto the script application, the Finder will pass a reference to the folder to our script in the variable myFolder. Then, we call the main function as above, passing it the reference to the folder.

  5. on batchProcess(myCurrentFolder)

    Here, we begin our function, batchProcess, and tell AppleScript to store any value passed to it in the variable myCurrentFolder.

  6. tell application "Finder"

    Next, we let AppleScript know that we want to talk to the Finder.

  7. set myFolderContents to list folder myCurrentFolder without invisibles

    We ask the Finder to return a list of items contained in the folder referred to by myCurrentFolder and store it in myFolderContents.

  8. repeat with myFile in myFolderContents

    Now we begin a repeat loop, placing one item from the list myFolderContents into the variable myFile for each loop until we reach the end of the list.

  9. if kind of alias (myCurrentFolder & myFile as text) is "folder" then

    We ask the Finder for the type of item myFile in the folder referred to by myCurrentFolder. If it's a folder, then our script will execute the next line.

  10. batchProcess(myCurrentFolder & myFile as text) of me

    If our function encounters another folder, it will call itself, passing a reference to the enclosed folder. In this way, our script processes all folders nested inside of the original.

  11. else
      tell application "BBEdit 4.5"

    If the item myFile in the folder isn't itself a folder, we begin talking to BBEdit.

  12. open (myCurrentFolder & myFile as text)

    First, we ask BBEdit to open the item myFile inside of the folder myCurrentFolder. We have to coerce the values to text to make sure AppleScript doesn't evaluate them as a list, which would generate an error.

  13. replace Every Occurrence searching for tab & "+" using "" with grep and start at top
    replace Every Occurrence searching for " +" using " " with grep and start at top

    Now BBEdit replaces all tabs with a null string and all multiple space characters with a single space, eliminating the file-bloating code indentation that many visual editors add to HTML files.

  14. close front window saving in (myCurrentFolder & myFile as text)
    end tell
    end if
    end repeat
    end tell
    end batchProcess

    Finally, we have BBEdit save the changed file over itself and conclude our conversation, our if statement, our repeat loop, and our function.

Code w1.6. This script removes all tabs and multiple spaces in a file, eliminating the unneeded code indentation added by most visual editors.


Note: Lines wrapped with red arrows should be typed on a single line.

Fig. w1.8  displays the Get Info windows for a file before and after processing, showing the file size difference in bytes.